#ifndef SWEGL_MATH_FUNCTIONS_H
#define SWEGL_MATH_FUNCTIONS_H

#include "swegl/common.h"
#include <cmath>

namespace swegl
{

template<typename T> static const T &Min(const T &a, const T &b);
template<typename T> static const T &Max(const T &a, const T &b);
template<typename T> static const T &Clamp(const T &v, const T &min, const T &max);
template<typename T> static void Swap(T &a, T &b);

static float RadToDeg(float angle);
static float DegToRad(float angle);

static float Sin(float angle);
static float Cos(float angle);
static void SinCos(float angle, float &sine, float &cosine);
static float Tan(float angle);

static float Sqrt(float value);
static float InvSqrt(float value);

static sint IAbs(sint value);
static float Abs(float value);
//static float Floor(float value);
//static float Ceil(float value);
template<typename T> static T Floor(float value);
template<typename T> static T Ceil(float value);

const float Pi                = 3.14159265358979323846f;
const float TwoPi             = 2.0f * Pi;
const float HalfPi            = 0.5f * Pi;
const float OneFourthPi       = 0.25f * Pi;
const float SqrtTwo           = 1.41421356237309504880f;
const float SqrtThree         = 1.73205080756887729352f;
const float DegToRadMulFactor = Pi / 180.0f;
const float RadToDegMulFactor = 180.0f / Pi;
/*
static const float Pi; // pi
static const float TwoPi; // 2 * pi
static const float HalfPi; // pi / 2
static const float OneFourthPi; // pi / 4
static const float SqrtTwo; // sqrt(2)
static const float SqrtThree; // sqrt(3)
static const float DegToRadMulFactor; // degree to radian multiplier factor
static const float RadToDegMulFactor; // radian to degree multiplier factor
*/
template<typename T>
inline const T &Min(const T &a, const T &b)
{
	return a < b ? a : b;
}

template<typename T>
inline const T &Max(const T &a, const T &b)
{
	return a > b ? a : b;
}

template<typename T>
inline const T &Clamp(const T &v, const T &min, const T &max)
{
	return Min(max, Max(min, v));
}

template<typename T>
inline void Swap(T &a, T &b)
{
	T tmp(a);
	a = b;
	b = tmp;
}

inline float RadToDeg(float angle)
{
	return angle * RadToDegMulFactor;
}

inline float DegToRad(float angle)
{
	return angle * DegToRadMulFactor;
}

inline float Sin(float angle)
{
	return ::sinf(angle);
}

inline float Cos(float angle)
{
	return ::cosf(angle);
}

inline void SinCos(float angle, float &sine, float &cosine)
{
#if 0
	// intel x86 optimized version
	_asm
	{
		fld     a
		fsincos
		mov     ecx, c
		mov     edx, s
		fstp    dword ptr [ecx]
		fstp    dword ptr [edx]
	}
#else
	sine = ::sinf(angle);
	cosine = ::cosf(angle);
#endif
}

inline float Tan(float angle)
{
	return ::tanf(angle);
}

inline float Sqrt(float value)
{
	sweglAssert(value >= 0.0f);
	return ::sqrtf(value);
}

inline float InvSqrt(float value)
{
	sweglAssert(value > 0.0f);
	return 1.0f / ::sqrtf(value);
}

inline sint IAbs(sint value)
{
   sint x = value >> 31;
   return (value ^ x) - x;
}

inline float Abs(float value)
{
	sint32 tmp = *reinterpret_cast<sint32 *>(&value);
	tmp &= 0x7FFFFFFF;
	return *reinterpret_cast<float *>(&tmp);
}
/*
inline float Floor(float value)
{
	return ::floorf(value);
}*/

template<typename T>
inline T Floor(float value)
{
	return static_cast<T>(::floorf(value));
}

template<typename T>
inline T Ceil(float value)
{
	return static_cast<T>(::ceilf(value));
}
/*
inline float Ceil(float value)
{
	return ::floorf(value);
}*/

//#define min    use_Math_Min
//#define max    use_Math_Max
#define clamp  use_Math_Clamp
#define sin    use_Math_Sin
#define sinf   use_Math_Sin
#define cos    use_Math_Cos
#define cosf   use_Math_Cos
#define tan    use_Math_Tan
#define tanf   use_Math_Tan
#define asin   use_Math_ASin
#define asinf  use_Math_ASin
#define acos   use_Math_ACos
#define acosf  use_Math_ACos
#define atan   use_Math_ATan
#define atanf  use_Math_ATan
#define sqrt   use_Math_Sqrt
#define sqrtf  use_Math_Sqrt
//#define abs    use_Math_Abs
#define fabs   use_Math_Abs
#define floor  use_Math_Floor
#define floorf use_Math_Floor
#define ceil   use_Math_Ceil
#define ceilf  use_Math_Ceil

}

#endif
